#ifndef RP2OBJLOADER_H
#define RP2OBJLOADER_H

#include "Rp2MtlLoader.h"
#include "Rp2ModelMesh.h"
#include "Rp2Vector2.h"

namespace Rp2
{

class ObjLoader
{
public:
	ObjLoader(const std::string& filename);
	~ObjLoader();

    enum ErrorCode
    {
        EC_SUCCESSFUL,
        EC_LOGFILE_OPEN_FAILED,
        EC_FILE_OPEN_FAILED,
        EC_NO_TOKENS,
        EC_TOO_FEW_TOKENS,
        EC_TOO_MANY_TOKENS,
        EC_UNEXPECTED_TOKEN,
        EC_NOT_YET_IMPLEMENTED,
        EC_FAILED_TO_LOAD_MATERIALS,
        EC_FAILED_TO_FIND_MATERIAL,
        EC_INVALID_VERTEX,
        EC_MAX_ERROR_CODES
    };

    class Vertex
    {
    public:
        Vertex ()
            :
            PosIndex(0),
            TcdIndex(0),
            NorIndex(0)
        {
        }

        bool operator< (const Vertex& vertex) const
        {
            if (PosIndex < vertex.PosIndex)
            {
                return true;
            }
            if (PosIndex > vertex.PosIndex)
            {
                return false;
            }
            if (TcdIndex < vertex.TcdIndex)
            {
                return true;
            }
            if (TcdIndex > vertex.TcdIndex)
            {
                return false;
            }
            if (NorIndex < vertex.NorIndex)
            {
                return true;
            }
            return false;
        }

        int PosIndex, TcdIndex, NorIndex;
    };

    class Face
    {
    public:
        std::vector<Vertex> Vertices;
    };

    class Mesh
    {
    public:
        int MtlIndex;
        std::vector<Face> Faces;
    };

    class Group
    {
    public:
        std::string Name;
        int PosStart, TcdStart, NorStart;
        std::vector<Mesh> Meshes;
    };

	static std::vector<TriMesh*>* LoadObj(const std::string& filename, std::vector<TriMesh*>& akMesh);
    inline ErrorCode GetCode () const;
    inline const std::vector<MtlLoader::Material>& GetMaterials () const;
    inline const std::vector<Group>& GetGroups () const;
    inline const std::vector<Vector3f>& GetPositions () const;
    inline const std::vector<Vector2f>& GetTCoords () const;
    inline const std::vector<Vector3f>& GetNormals () const;

private:
    void GetTokens (const std::string& line, std::vector<std::string>& tokens);
    bool GetMaterialLibrary (const std::vector<std::string>& tokens);
    bool GetDefaultGroup (const std::vector<std::string>& tokens);
    bool GetPosition (const std::vector<std::string>& tokens);
    bool GetTCoord (const std::vector<std::string>& tokens);
    bool GetNormal (const std::vector<std::string>& tokens);
    bool GetGroup (const std::vector<std::string>& tokens);
    bool GetMaterialAndMesh (const std::vector<std::string>& tokens);
    bool GetFace (const std::vector<std::string>& tokens);

    ErrorCode mCode;
    FILE* mLogFile;
    std::vector<MtlLoader::Material> mMaterials;
    int mCurrentGroup, mCurrentPos, mCurrentTcd, mCurrentNor;
    int mCurrentMtl, mCurrentMesh;
    std::vector<Group> mGroups;
    std::vector<Vector3f> mPositions;
    std::vector<Vector2f> mTCoords;
    std::vector<Vector3f> mNormals;
	// for convenience when save to native
	std::vector<Face> mFaces;
	std::vector<Mesh> mMeshes;
	std::vector<Vertex> mVertices;

    static const char* msCodeString[EC_MAX_ERROR_CODES];
	ModelMesh* mpkMoMesh;
};

#include "Rp2ObjLoader.inl"

}

#endif